package StratelegoGame;

import SwingLib.Message;
import TwoClientGameConnection.TCGApplicationProtocol;
import TwoClientGameConnection.TCGClient;
import TwoClientGameConnection.TCGClientProtocol;
import TwoClientGameConnection.TCGConnectionException;
import TwoClientGameConnection.TCGGeneralException;
import TwoClientGameConnection.TCGMessage;

/**
 * This class defines the protocol for messages sent
 * between the client and the server.
 * @author Jace Ferguson
 * @filename ClientProtocol.java
 *
 */
public class ClientProtocol implements TCGClientProtocol {
	private TCGClient client;
	private GameBoard2DApplicationProtocol app;
	private GameBoard2DLogic gameLogic;
	private String host;
	private int port;
	/**
	 * Constructor to establish a client object.
	 * All exception message will be ported to the error
	 * handler method defined by the interface.
	 * @param address	String Internet host to the server.
	 * @param port	int	Port number to use when attempting to connect.
	 */
	public ClientProtocol(String address, int port)
	{
		this.host = address;
		this.port = port;
	}

	/**
	 * Try to connect to the server.
	 */
	public void connectToServer()
	{
		try
		{
			Thread clientStart = new Thread(this.client = new TCGClient(this.host, this.port));
			this.client.setProtocol(this);
			clientStart.setDaemon(true);
			clientStart.start();
		}
		catch(TCGGeneralException e)
		{
			this.client = null;
			this.handleError(new TCGConnectionException("Unable to connect client to server. Reason: " + e));
		}
	}

	/**
	 * Get the game logic object or null if it
	 * hasn't been set.
	 * @return	GameBoardLogic | null
	 */
	public GameBoardLogic getGameLogic()
	{
		return this.gameLogic;
	}

	/**
	 * Display exceptions that the client throws as Messages.
	 * @param	e	TCGGeneralException	exception that was thrown
	 */
	public void handleError(TCGGeneralException e)
	{
		new Message("Client Exception: " + e, "Client Exception", Message.WARNING);
	}

	/**
	 * Handle messages received by the client.
	 * @param	TCGMessage	message received.
	 */
	public void handleMessage(TCGMessage message)
	{
		//Chat message was received.
		if(message.getCommand().equals("chatMessage"))
		{
			this.receiveChatMessage(message);
		}
		//A connected verification was received
		else if(message.getCommand().equals("CONNECTED"))
		{
			this.receiveWelcome(message);
		}
		//Start new game signal was received
		else if(message.getCommand().equals("startNewGame"))
		{
			this.receiveStartNewGame(message);
		}
		//Commence the game message was received
		else if(message.getCommand().equals("commencePlay"))
		{
			this.receiveCommencePlay(message);
		}
		//Tells the player to start turn
		else if(message.getCommand().equals("startTurn"))
		{
			//this.receiveStartActiveTurn(message);
			this.receiveAcceptTurn(message);
		}
		//Tells the player if they are first or second to move.
		else if(message.getCommand().equals("firstMove"))
		{
			this.receiveFirstMove(message);
		}
		//Tells game logic to remove a piece with a space id.
		else if(message.getCommand().equals("removePieceWithId"))
		{
			this.receiveRemovePieceWithId(message);			 
		}
		//Tells game to move a piece from one space to another with space ids.
		else if(message.getCommand().equals("movePieceWithIds"))
		{
			this.receiveMovePieceWithIds(message);
		}
		//Tells the game to create a piece object and add it to a space with the space id.
		else if(message.getCommand().equals("addPieceWithValueToSpace"))
		{
			this.receiveAddPieceWithValueToSpace(message);
		}
		//Tell the player the game has ended.
		else if(message.getCommand().equals("gameOver"))
		{
			this.receiveGameOver(message);
		}
		//Alert the player to an end game message.
		else if(message.getCommand().equals("retrieveResultMessage"))
		{
			this.receiveResultMessage(message);
		}
		//Alert the player to take its turn.
		else if(message.getCommand().equals("yourTurn"))
		{
			this.receiveAcceptTurn(message);
		}
		//Receive a message that a piece was attacked
		else if(message.getCommand().equals("receiveAttackMessage"))
		{
			this.receiveAttackMessage(message);
		}
		//Receive a message that shows active player
		else if(message.getCommand().equals("playerCheck"))
		{
			this.receivePlayerCheck(message);
		}
	}

	/**
	 * Throw a warning that no application could be found
	 * to execute a command on.
	 * @param message	String
	 */
	public void noApp(String message)
	{
		new Message("No App: " + message, "Client Exception", Message.WARNING);
	}

	/**
	 * Throw a warning that no game logic could be found to 
	 * execute a command on.
	 * @param message	String
	 */
	public void noGameLogic(String message)
	{
		new Message("No Game Logic: " + message, "Client Exception", Message.WARNING);
	}

	/**
	 * Start the local players active turn.
	 * @param message	TCGMessage
	 */
	public void receiveAcceptTurn(TCGMessage message)
	{
		if(this.gameLogic != null)
		{
			if(this.gameLogic.getStatus() != GameBoard2DLogic.PLAYER_GAME_ENDED)
			{
				this.gameLogic.startTurn();
				if(this.app != null)
					this.app.updateStatus("Your turn");
				else
					this.noApp("Unable to update status.");
			}
		}
		else
			this.noGameLogic("Accept Turn Failed.");
	}

	/**
	 * Add a piece defined by it's value to a space.
	 * @param message	TCGMessage
	 */
	public void receiveAddPieceWithValueToSpace(TCGMessage message)
	{
		Integer value = Integer.valueOf(message.getParameterValue("pieceValue"));
		Integer spaceId = Integer.valueOf(message.getParameterValue("targetSpace"));
		if(this.gameLogic != null)
		{
			this.gameLogic.addOpponentPieceToSpace(value, spaceId);
		}
		else
			this.noGameLogic("Add piece failed.");
	}

	/**
	 * Show a message that a player was attacked
	 * @param message	TCGMessage
	 */
	public void receiveAttackMessage(TCGMessage message)
	{
		if(this.gameLogic != null)
			this.gameLogic.displayAttackMessage(message.getParameterValue("attackMessage"));
		else
			this.noGameLogic("Show attack message failed.");
	}

	/**
	 * Receive a text message and update the application
	 * @param message	TCGMessage
	 */
	public void receiveChatMessage(TCGMessage message)
	{		 
		if(this.app != null)
			this.app.addMessageToDisplay(message.getParameterValue("message"));
		else
			this.noApp("Unable to display chat message.");
	}

	/**
	 * Receive the command to start game play
	 * @param message	TCGMessage
	 */
	public void receiveCommencePlay(TCGMessage message)
	{
		if(this.gameLogic != null)
			this.gameLogic.commencePlay();
		else
			this.noGameLogic("Unable to commence play.");
	}

	/**
	 * Receive instructions for the first move and display instructions.
	 * @param message	TCGMessage
	 */
	public void receiveFirstMove(TCGMessage message)
	{
		new Message(message.getParameterValue("startMessage"), "Turn Order", Message.INFO);
	}

	/**
	 * Receive the command that the game has ended.
	 * @param message	TCGMessage
	 */
	public void receiveGameOver(TCGMessage message)
	{
		if(this.gameLogic != null)
			this.gameLogic.endGame();
		else
			this.noGameLogic("Unable to end game.");
		if(this.app != null)
			this.app.updateStatus("Game Over");
		else
			this.noApp("Unable to update status.");
	}

	/**
	 * Move a piece from one space to another based
	 * on space id.
	 * @param message	TCGMessage
	 */
	public void receiveMovePieceWithIds(TCGMessage message)
	{
		//recreate the ids.
		Integer srcIntId = Integer.valueOf(message.getParameterValue("srcId"));
		Integer tarIntId = Integer.valueOf(message.getParameterValue("tarId"));
		if(this.gameLogic != null)
			this.gameLogic.movePieceFromSpaceToSpace(srcIntId, tarIntId);
		else
			this.noGameLogic("Unable to move piece.");
	}

	/**
	 * Receive if both players are on the server.
	 * @param message	TCGMessage
	 */
	public void receivePlayerCheck(TCGMessage message)
	{
		Integer playerCheck = Integer.valueOf(message.getParameterValue("playerCheck"));
		if(playerCheck == 1)
		{
			if(this.gameLogic != null)
				this.gameLogic.playersConnected(true);
			else
				this.noGameLogic("Unable to show players connected.");
		}
		else
		{
			if(this.gameLogic != null)
				this.gameLogic.playersConnected(false);
			else
				this.noGameLogic("Unable to show players connected.");

		}
	}

	/**
	 * Remove a piece from a space given the space id.
	 * @param message	TCGMessage
	 */
	public void receiveRemovePieceWithId(TCGMessage message)
	{
		int spaceId = Integer.valueOf(message.getParameterValue("spaceId"));
		if(this.gameLogic != null)
			this.gameLogic.removePieceFromSpace(spaceId);
		else
			this.noGameLogic("Unable to remove piece.");
	}

	/**
	 * Reciece the results for the end of the game.
	 * @param message	TCGMessage
	 */
	public void receiveResultMessage(TCGMessage message)
	{
		if(this.gameLogic != null)
			this.gameLogic.displayResultMessage(message.getParameterValue("message"));
		else
			this.noGameLogic("Unable to receive game results.");
	}

	/**
	 * Receive the signal to start a new game.
	 * @param message	TCGMessage
	 */
	public void receiveStartNewGame(TCGMessage message)
	{
		if(this.gameLogic != null)
			this.gameLogic.startNewGame();
		else
			this.noGameLogic("Unable to start new game.");
	}

	/**
	 * Receive the server connection welcome.
	 * @param message	TCGMessage
	 */
	public void receiveWelcome(TCGMessage message)
	{
		if(this.app != null)
		{
			this.app.updateStatus("Connected: " + message.getParameterValue("serverWelcome"));
			this.app.initializeGame();
		}
		else
			this.noApp("Unable to update status.");
	}

	/**
	 * Send a message about an attack on a opponents player
	 * @param message	String
	 */
	public void sendAttackMessage(String message)
	{
		if(this.client != null)
		{
			TCGMessage attackMessage = new TCGMessage(this.client.getMessageId());
			attackMessage.setCommand("sendAttackMessageToOpponent");
			attackMessage.addParameter("attackMessage", message);
			this.client.sendMessage(attackMessage);
		}
		else
			this.handleError(new TCGGeneralException("No client to receive message. From: sendAttackMessage."));
	}

	/**
	 * Send a message to change a user's alias
	 * @param newAlias	String
	 */
	public void sendChangeAlias(String newAlias)
	{
		if(this.client != null)
		{
			TCGMessage changeAlias = new TCGMessage(this.client.getMessageId());
			changeAlias.setCommand("CHANGEALIAS");
			changeAlias.addParameter("alias", newAlias);
			this.client.sendMessage(changeAlias);
		}
		else
			this.handleError(new TCGGeneralException("No client to receive message. From: sendChangeAlias."));
	}

	/**
	 * Send a chat message
	 * @param message	String
	 */
	public void sendChatMessage(String message)
	{
		if(this.client != null)
		{
			TCGMessage chatMessage = new TCGMessage(this.client.getMessageId());
			chatMessage.setMessageType(TCGMessage.MESSAGE_TYPE_LOW);
			chatMessage.addParameter("message", message);
			chatMessage.setCommand("chatMessage");
			this.client.sendMessage(chatMessage);
		}
		else
			this.handleError(new TCGGeneralException("No client to receive message. From: sendChatMessage."));
	}

	/**
	 * Send end game signal.
	 */
	public void sendEndGame()
	{
		if(this.app != null)
			this.app.updateStatus("Game Over");
		else
			this.noApp("Unable to update status.");

		if(this.client != null)
		{
			TCGMessage endGame = new TCGMessage(this.client.getMessageId());
			endGame.setCommand("gameOver");
			this.client.sendMessage(endGame);
		}
		else
			this.handleError(new TCGGeneralException("No client to receive message. From: sendEndGame."));
	}

	/**
	 * Send signal to other client to start turn.
	 */
	public void sendEndTurn()
	{
		if(this.app != null)
			this.app.updateStatus("Waiting for opponent");
		else
			this.noApp("Unable to update status.");
		if(this.client != null)
		{
			TCGMessage endTurn = new TCGMessage(this.client.getMessageId());
			endTurn.setCommand("yourTurn");
			this.client.sendMessage(endTurn);
		}
		else
			this.handleError(new TCGGeneralException("No client to receive message. From: sendEndTurn."));
	}

	/**
	 * Send a message to have the client move a piece from the source
	 * space to the target space.
	 * @param srcSpaceId	Integer
	 * @param tarSpaceId	Integer
	 */
	public void sendMovePieceFromSpaceToSpace(Integer srcSpaceId, Integer tarSpaceId)
	{
		if(this.client != null)
		{
			TCGMessage moveMessage = new TCGMessage(this.client.getMessageId());
			moveMessage.setCommand("movePieceWithIds");
			moveMessage.addParameter("srcId", srcSpaceId.toString());
			moveMessage.addParameter("tarId", tarSpaceId.toString());
			this.client.sendMessage(moveMessage);
		}
		else
			this.handleError(new TCGGeneralException("No client to receive message. From: sendMovePieceFromSpaceToSpace."));
	}

	/**
	 * Send signal to add a piece with the given value to the target space.
	 * @param pieceValue	Integer
	 * @param targetId	Integers
	 */
	public void sendPieceToAdd(Integer pieceValue, Integer targetId)
	{
		if(this.client != null)
		{
			TCGMessage addMessage = new TCGMessage(this.client.getMessageId());
			addMessage.setCommand("addPieceWithValueToSpace");
			addMessage.addParameter("pieceValue", pieceValue.toString());
			addMessage.addParameter("targetSpace", targetId.toString());
			this.client.sendMessage(addMessage);
		}
		else
			this.handleError(new TCGGeneralException("No client to receive message. From: sendPieceToAdd."));
	}

	/**
	 * Send a request to see if both players are on the server.
	 */
	public void sendPlayerCheck()
	{
		if(this.client != null)
		{
			TCGMessage playerCheck = new TCGMessage(this.client.getMessageId());
			playerCheck.setCommand("checkForAllPlayers");
			this.client.sendMessage(playerCheck);
		}
		else
			this.handleError(new TCGGeneralException("No client to receive message. From: sendChatMessage."));
	}

	/**
	 * Send request to close the connection.
	 */
	public void sendQuitClient()
	{
		if(this.client != null)
		{
			TCGMessage quit = new TCGMessage(this.client.getMessageId());
			quit.setCommand("QUIT");
			this.client.sendMessage(quit);
		}
		else
			this.handleError(new TCGGeneralException("No client to receive message. From: sendQuitClient."));
	}

	/**
	 * Send request stating player is ready to start a new game.
	 */
	public void sendReadyToStartNewGame()
	{
		if(this.client != null)
		{
			TCGMessage readyToPlay = new TCGMessage(this.client.getMessageId());
			readyToPlay.setCommand("readyToStartNewGame");
			this.client.sendMessage(readyToPlay);
		}
		else
			this.handleError(new TCGGeneralException("No client to receive message. From: sendReadyToStartNewGame."));
	}

	/**
	 * Send request for client to remove a piece from a give space.
	 * @param translatedSpaceId	Integer
	 */
	public void sendRemovePieceFromSpace(Integer translatedSpaceId)
	{
		if(this.client != null)
		{
			TCGMessage removeMessage = new TCGMessage(this.client.getMessageId());
			removeMessage.setCommand("removePieceWithId");
			removeMessage.addParameter("spaceId", translatedSpaceId.toString());
			this.client.sendMessage(removeMessage);
		}
		else
			this.handleError(new TCGGeneralException("No client to receive message. From: sendRemovePieceFromSpace."));
	}

	/**
	 * Send an end game result message
	 * @param message	String
	 */
	public void sendResultMessage(String message)
	{
		if(this.client != null)
		{
			TCGMessage result = new TCGMessage(this.client.getMessageId());
			result.setCommand("retrieveResultMessage");
			result.addParameter("message", message);
			this.client.sendMessage(result);
		}
		else
			this.handleError(new TCGGeneralException("No client to receive message. From: sendResultMessage."));
	}

	/**
	 * Send notice that the client is ready to begin game play
	 */
	public void sendWaitingToBegin()
	{
		if(this.app != null)
			this.app.updateStatus("Waiting for opponent.");
		else
			this.noApp("Unable to update status.");
		if(this.client != null)
		{
			TCGMessage waitingToBegin = new TCGMessage(this.client.getMessageId());
			waitingToBegin.setCommand("waitingToBegin");
			this.client.sendMessage(waitingToBegin);
		}
		else
			this.handleError(new TCGGeneralException("No client to receive message. From: sendChatMessage."));
	}

	/**
	 * Set the application handler for the client protocol
	 * @param app	TCGApplicationProtocol
	 */
	public void setApplicationHandler(TCGApplicationProtocol app)
	{
		this.app = (GameBoard2DApplicationProtocol)app;
	}

	/**
	 * Set the client for the protocol
	 * @param	TCGClient 
	 */
	public void setClient(TCGClient client)
	{
		this.client = client;
	}

	/**
	 * Set the game logic the protocol should use.
	 * @param logic GameBoardLogic
	 */
	public void setGameLogic(GameBoardLogic logic)
	{
		this.gameLogic = (GameBoard2DLogic)logic;
	}
}


